import pandas as pd
import numpy as np
import glob
from typing import List, Dict
from loguru import logger

# 选股策略类

class StockSelector:
    @staticmethod
    def load_orderbook_data(data_path: str) -> pd.DataFrame:
        """
        读取orderbook数据CSV文件并预处理
        参数:
            data_path: 数据文件路径模式，如"test/orderbook/data/hsnapshot_*.csv"
        返回:
            合并后的DataFrame
        """
        # 定义列名映射
        col_rename = {
            'tradeDate': 'trade_date',
            'market': 'market',
            'securityId': 'security_id',
            'preClosePx': 'pre_close',
            'openPx': 'open',
            'highPx': 'high',
            'lowPx': 'low',
            'tradePx': 'close',
            'numTrades': 'trades',
            'volumeTrade': 'volume',
            'amountTrade': 'amount',
            'preVolume': 'pre_volume',
            'multVol': 'mult_vol',
            'upperTimes': 'upper_times',
            'upperBidOrdQty': 'upper_bid_qty',
            'upperBidOrdCnt': 'upper_bid_cnt',
            'lowerOfferOrdQty': 'lower_offer_qty',
            'lowerOfferOrdCnt': 'lower_offer_cnt',
            'upperOfferOrdQty': 'upper_offer_qty',
            'upperOfferOrdCnt': 'upper_offer_cnt',
            'lowerBidOrdQty': 'lower_bid_qty',
            'lowerBidOrdCnt': 'lower_bid_cnt',
            'yesUpperOfferOrdQty': 'prev_upper_offer_qty',
            'yesUpperOfferOrdCnt': 'prev_upper_offer_cnt',
            'upperPx': 'upper_px',
            'lowerPx': 'lower_px',
            'securityName': 'security_name',
            'netamt': 'netamt',
            'bignetamt': 'bignetamt'
        }

        # 读取所有匹配的CSV文件
        all_files = glob.glob(data_path)
        df_list = []
        
        for file in all_files:
            df = pd.read_csv(file)
            # 重命名列
            df.rename(columns=col_rename, inplace=True)
            # 添加文件名作为数据来源标记
            df['source_file'] = file.split('\\')[-1]
            df_list.append(df)
            
        # 合并所有数据
        combined_df = pd.concat(df_list, ignore_index=True)
        
        # 转换日期格式
        if 'trade_date' in combined_df.columns:
            try:
                combined_df['trade_date'] = pd.to_datetime(
                    combined_df['trade_date'].astype(str), format='%Y%m%d',errors='coerce'
                )
                combined_df = combined_df.dropna(subset=['trade_date']) 
            except Exception as e:
                logger.error(f"日期格式转换错误: {str(e)}")

         # 新增：检查关键字段是否为0，如果是则删除该行
        key_fields = ['open', 'high', 'low', 'close', 'volume']
        for field in key_fields:
            if field in combined_df.columns:
                combined_df = combined_df[combined_df[field] != 0]

        return combined_df

    @staticmethod
    def breakout_platform(data: pd.DataFrame, lookback_days: int = 15) -> bool:
        """
        突破平台策略
        条件1: 最近15日收盘价在60日均线±5%范围内(如果数据不足60日，使用最大可用数据)
        条件2: 放量上涨(成交量>1.5倍5日均量且收盘>开盘)
        条件3: 之前任意一天收盘价与60日均线偏离在-5%~20%之间
        """
        if len(data) < lookback_days:
            return False
            
        # 计算均线，使用min(60, 可用数据量)
        ma_window = min(60, len(data))
        ma60 = data['close'].rolling(ma_window).mean().iloc[-1]
        
        # 条件1: 最近15日收盘价在60日均线±5%范围内
        recent_close = data['close'].iloc[-lookback_days:]
        if not all((recent_close - ma60).abs() / ma60 <= 0.05):
            return False
            
        # 条件2: 放量上涨
        avg_vol5 = data['volume'].rolling(5).mean().iloc[-1]
        last_bar = data.iloc[-1]
        if not (last_bar['volume'] > 1.5 * avg_vol5 and last_bar['close'] > last_bar['open']):
            return False
            
        # 条件3: 检查历史偏离
        for i in range(1, lookback_days + 1):
            close = data['close'].iloc[-i]
            deviation = (close - ma60) / ma60
            if -0.05 <= deviation <= 0.2:
                return True
                
        return False

    @staticmethod
    def airfield_strategy(data: pd.DataFrame, lookback_days: int = 15) -> bool:
        """
        停机坪策略
        条件1: 最近15日有涨幅>9.5%且放量上涨(成交量>1.8倍15日均量)
        条件2: 下个交易日必须高开且收盘上涨，涨幅<3%
        条件3: 接下来2-3个交易日必须高开且收盘上涨，涨幅<3%，涨跌幅在5%内
        """
        min_days = lookback_days + 3
        if len(data) < min_days:
            return False
            
        # 条件1: 寻找大阳线日
        vol_window = min(15, len(data))  # 使用最大可用数据量
        avg_vol15 = data['volume'].rolling(vol_window).mean()
        has_big_rise = False
        rise_day_idx = -1
        
        for i in range(1, lookback_days + 1):
            bar = data.iloc[-i]
            if bar['open'] <= 0:
                continue
                
            rise = (bar['close'] - bar['open']) / bar['open']
            if rise > 0.095 and bar['volume'] > 1.8 * avg_vol15.iloc[-i]:
                has_big_rise = True
                rise_day_idx = -i
                break
                
        if not has_big_rise:
            return False
            
        # 条件2: 下个交易日验证
        next_day1 = data.iloc[rise_day_idx + 1]
        if next_day1['open'] <= 0:
            return False
            
        rise1 = (next_day1['close'] - next_day1['open']) / next_day1['open']
        prev_close = data.iloc[rise_day_idx]['close']
        if not (next_day1['open'] > prev_close and 0 < rise1 < 0.03):
            return False
            
        # 条件3: 接下来2-3个交易日验证
        for i in [2, 3]:
            next_day = data.iloc[rise_day_idx + i]
            prev_day = data.iloc[rise_day_idx + i - 1]
            
            if next_day['open'] <= 0 or prev_day['close'] <= 0:
                return False
                
            rise = (next_day['close'] - next_day['open']) / next_day['open']
            change = (next_day['close'] - prev_day['close']) / prev_day['close']
            
            if not (next_day['open'] > prev_day['close'] and 
                    0 < rise < 0.03 and 
                    abs(change) < 0.05):
                return False
                
        return True

    @staticmethod
    def volume_limit_down(data: pd.DataFrame) -> bool:
        """
        放量跌停策略
        条件1: 跌幅>9.5%
        条件2: 成交额不低于2亿
        条件3: 成交量至少是5日平均成交量的4倍
        """
        if len(data) < 5:
            return False

        last_bar = data.iloc[-1]
        if last_bar['open'] <= 0:
            return False
            
        # 条件1: 跌幅>9.5%
        drop = (last_bar['open'] - last_bar['close']) / last_bar['open']
        if drop <= 0.095:
            return False
            
        # 条件2: 成交额不低于2亿
        amount = last_bar['close'] * last_bar['volume']
        if amount < 2e8:
            return False
            
        # 条件3: 成交量倍数
        avg_vol5 = data['volume'].rolling(5).mean().iloc[-1]
        if last_bar['volume'] < 4 * avg_vol5:
            return False
            
        return True

    @staticmethod
    def no_large_drawdown(data: pd.DataFrame, period: int = 60) -> bool:
        """
        无大幅回撤策略
        条件1: 当日收盘价比60日前的收盘价的涨幅小于0.6%
        条件2: 检查60日内是否有大幅回撤(单日跌幅>7%,高开低走>7%,两日累计跌幅>10%)
        """
        period = min(period, len(data) - 1)  # 使用最大可用数据量
        if len(data) < period + 1:
            return False
            
        # 条件1: 60日涨幅<0.6%
        price60 = data['close'].iloc[-period-1]
        rise = (data['close'].iloc[-1] - price60) / price60
        if rise >= 0.006:
            return False
            
        # 条件2: 检查回撤
        for i in range(1, period + 1):
            bar = data.iloc[-i]
            prev_bar = data.iloc[-i-1] if i < len(data)-1 else None
            
            # 单日跌幅>7%
            if prev_bar is not None:
                daily_drop = (prev_bar['close'] - bar['close']) / prev_bar['close']
                if daily_drop >= 0.07:
                    return False
                    
            # 高开低走>7%
            if bar['open'] > 0:
                open_close_drop = (bar['open'] - bar['close']) / bar['open']
                if open_close_drop >= 0.07:
                    return False
                    
            # 两日累计跌幅>10%
            if i > 1 and prev_bar is not None:
                two_day_drop = (data.iloc[-i-1]['close'] - bar['close']) / data.iloc[-i-1]['close']
                if two_day_drop >= 0.1:
                    return False
                    
        return True

    @staticmethod
    def flag_pattern(data: pd.DataFrame, min_trading_days: int = 60) -> bool:
        """
        高而窄的旗形策略
        条件1: 至少上市交易60日(如果数据不足则使用最大可用数据)
        条件2: 当日收盘价/之前24~10日的最低价>=1.9
        条件3: 之前24~10日必须连续两天涨幅大于等于9.5%
        """
        min_trading_days = min(min_trading_days, len(data))  # 使用最大可用数据量
        if len(data) < min_trading_days:
            return False
            
        # 条件2: 计算24~10日最低价
        lookback_start = 24
        lookback_end = 10

        if len(data) < lookback_start:
            return False
            
        min_price = data['close'].iloc[-lookback_start:-lookback_end].min()
        
        if data['close'].iloc[-1] / min_price < 1.9:
            return False
            
        # 条件3: 检查连续大涨
        has_two_big_rises = False
        for i in range(lookback_start, lookback_end + 1, -1):
            if i < 2:
                continue
                
            bar1 = data.iloc[-i]
            bar2 = data.iloc[-i+1]
            
            if bar1['open'] <= 0 or bar2['open'] <= 0:
                continue
                
            rise1 = (bar1['close'] - bar1['open']) / bar1['open']
            rise2 = (bar2['close'] - bar2['open']) / bar2['open']
            
            if rise1 >= 0.095 and rise2 >= 0.095:
                has_two_big_rises = True
                break
                
        return has_two_big_rises

    @staticmethod
    def apply_strategy_to_all(data: pd.DataFrame, strategy_name: str = 'breakout_platform') -> pd.DataFrame:
        """
        对所有股票应用选股策略
        参数:
            data: 包含多只股票数据的DataFrame
            strategy_name: 策略名称 ('breakout_platform', 'airfield_strategy'等)
        返回:
            包含选股结果的DataFrame
        """
        if not hasattr(StockSelector, strategy_name):
            raise ValueError(f"无效的策略名称: {strategy_name}")

        # 按股票分组
        grouped = data.groupby('security_id')
        results = []
        
        for security_id, group in grouped:
            # 按日期排序确保时间序列正确
            group = group.sort_values('trade_date')
            
            try:
                if len(group) < 2:  # 确保数据长度至少为2
                    continue
                # 获取策略方法
                strategy_func = getattr(StockSelector, strategy_name)
                # 应用策略
                is_selected = strategy_func(group)
                
                if is_selected:
                    last_record = group.iloc[-1].copy()
                    last_record['strategy'] = strategy_name
                    
                    # 为三阴不破阳策略添加详细信息
                    if strategy_name == 'three_yin_not_break_yang':
                        # 寻找涨停日
                        limit_up_idx = -1
                        for i in range(1, 21):  # 最近20天
                            if group.iloc[-i]['close'] == group.iloc[-i]['upper_px']:
                                limit_up_idx = -i
                                break
                        
                        if limit_up_idx != -1:
                            limit_up_day = group.iloc[limit_up_idx]
                            # 计算3根阴线的累计跌幅
                            day1 = group.iloc[limit_up_idx + 1]
                            day3 = group.iloc[limit_up_idx + 3]
                            total_drop = (day1['open'] - day3['close']) / day1['open']
                            limit_up_rise = (limit_up_day['close'] - limit_up_day['open']) / limit_up_day['open']
                            
                            last_record['limit_up_date'] = limit_up_day['trade_date']
                            last_record['limit_up_price'] = limit_up_day['close'] * 0.0001
                            last_record['total_drop_pct'] = total_drop * 100
                            last_record['limit_up_rise_pct'] = limit_up_rise * 100
                    
                    # 为涨停揉搓线策略添加详细信息
                    elif strategy_name == 'limit_up_rubbing_line':
                        # 寻找涨停日
                        limit_up_idx = -1
                        for i in range(1, 21):  # 最近20天
                            if group.iloc[-i]['close'] == group.iloc[-i]['upper_px']:
                                limit_up_idx = -i
                                break
                        
                        if limit_up_idx != -1:
                            limit_up_day = group.iloc[limit_up_idx]
                            day1 = group.iloc[limit_up_idx + 1]  # 长上影线阴线
                            day2 = group.iloc[limit_up_idx + 2]  # 长下影线阳线
                            
                            # 计算揉搓线特征
                            day1_upper_shadow = (day1['high'] - max(day1['open'], day1['close'])) / max(day1['open'], day1['close']) * 100
                            day2_lower_shadow = (min(day2['open'], day2['close']) - day2['low']) / min(day2['open'], day2['close']) * 100
                            day1_volume_ratio = day1['volume'] / limit_up_day['volume']
                            day2_volume_ratio = day2['volume'] / limit_up_day['volume']
                            
                            last_record['limit_up_date'] = limit_up_day['trade_date']
                            last_record['limit_up_price'] = limit_up_day['close'] * 0.0001
                            last_record['day1_upper_shadow_pct'] = day1_upper_shadow
                            last_record['day2_lower_shadow_pct'] = day2_lower_shadow
                            last_record['day1_volume_ratio'] = day1_volume_ratio
                            last_record['day2_volume_ratio'] = day2_volume_ratio
                    
                    # 为涨停后收阴策略添加详细信息
                    elif strategy_name == 'limit_up_then_down':
                        # 寻找涨停日
                        limit_up_idx = -1
                        for i in range(1, 11):  # 最近10天
                            if group.iloc[-i]['high'] == group.iloc[-i]['upper_px']:
                                limit_up_idx = -i
                                break
                        
                        if limit_up_idx != -1:
                            limit_up_day = group.iloc[limit_up_idx]
                            next_day = group.iloc[limit_up_idx + 1]  # 涨停后次日
                            
                            last_record['limit_up_date'] = limit_up_day['trade_date']
                            last_record['limit_up_price'] = limit_up_day['close'] * 0.0001
                            last_record['next_day_close'] = next_day['close'] * 0.0001
                            last_record['next_day_open'] = next_day['open'] * 0.0001
                    
                    # 为涨停缩倍量策略添加详细信息
                    elif strategy_name == 'limit_up_shrinking_volume':
                        # 寻找涨停日
                        limit_up_idx = -1
                        for i in range(1, 21):  # 最近20天
                            if group.iloc[-i]['close'] == group.iloc[-i]['upper_px']:
                                limit_up_idx = -i
                                break
                        
                        if limit_up_idx != -1:
                            limit_up_day = group.iloc[limit_up_idx]
                            day1 = group.iloc[limit_up_idx + 1]  # 次日阴线缩量
                               
                            last_record['limit_up_date'] = limit_up_day['trade_date']
                            last_record['limit_up_price'] = limit_up_day['close'] * 0.0001
                            
                    elif strategy_name == 'oversold_rebound_technical_turning_point':
                         # 寻找涨停日
                        limit_up_idx = -1
                        for i in range(1, 21):  # 最近20天
                            if group.iloc[-i]['close'] == group.iloc[-i]['upper_px']:
                                limit_up_idx = -i
                                break
                        
                        if limit_up_idx != -1:
                            limit_up_day = group.iloc[limit_up_idx]
                            day1 = group.iloc[limit_up_idx + 1]  # 次日阴线缩量
                               
                            last_record['limit_up_date'] = limit_up_day['trade_date']
                            last_record['limit_up_price'] = limit_up_day['close'] * 0.0001
                    
                    results.append(last_record)
            except Exception as e:
                logger.error(f"处理股票 {security_id} 时出错: {str(e)}")
                continue
                
        return pd.DataFrame(results)

    @staticmethod
    def four_up_three_down(data: pd.DataFrame, min_trading_days: int = 60) -> bool:
        """
        四连板后三连跌策略
        条件1: 60日内有连续4个或以上涨停板
        条件2: 之后有连续2个或以上跌停板
        条件3: 当前价格回到4连板起点价格附近(±5%)
        """
        min_trading_days = min(min_trading_days, len(data))  # 使用最大可用数据量
        if len(data) < min_trading_days:
            return False

        #至少30个交易日数据
        if min_trading_days < 30:
            return False

        # 寻找连续4个或以上涨停板
        up_streak = 0
        up_start_idx = -1
        up_start_price = 0
        
        for i in range(1, min_trading_days + 1):
            bar = data.iloc[-i]
            if bar['close'] == bar['upper_px']:  # 涨停
                up_streak += 1
                if up_streak == 1:  # 记录第一个涨停板
                    up_start_idx = -i
                    up_start_price = bar['close']
            else:
                if up_streak >= 4:  # 找到4连板
                    break
                up_streak = 0
                
        if up_streak < 4:
            return False
            
        # 寻找连续2个或以上跌停板
        down_streak = 0
        for i in range(up_start_idx + 1, 0):  # 从4连板后开始检查
            bar = data.iloc[i]
            if bar['close'] == bar['lower_px']:  # 跌停
                down_streak += 1
                if down_streak >= 2:
                    break
            else:
                down_streak = 0
                
        if down_streak < 2:
            return False
            
        # 检查当前价格是否回到起点附近
        current_price = data.iloc[-1]['close']
        price_diff = abs(current_price - up_start_price) / up_start_price
        if price_diff > 0.05:  # 偏离超过5%
            return False
            
        return True

    @staticmethod
    def near_high_with_limited_ups(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        接近高点且有2-3个涨停策略
        条件1: 20日内无连续涨停
        条件2: 20日内有2-3个涨停
        条件3: 当前价格接近20日高点(±3%)
        """
        if len(data) < lookback_days:
            return False
            
        # 条件1: 检查20日内无连续涨停
        consecutive_ups = 0
        for i in range(1, lookback_days + 1):
            bar = data.iloc[-i]
            if bar['close'] == bar['upper_px']:  # 涨停
                consecutive_ups += 1
                if consecutive_ups >= 2:  # 发现连续涨停
                    return False
            else:
                consecutive_ups = 0
                
        # 条件2: 统计20日内涨停次数(2-3次)
        up_count = 0
        for i in range(1, lookback_days + 1):
            bar = data.iloc[-i]
            if bar['close'] == bar['upper_px']:
                up_count += 1
                
        if up_count < 2 or up_count > 3:
            return False
            
        # 条件3: 当前价格接近20日高点(±3%)
        recent_high = data['high'].iloc[-lookback_days:].max()
        current_price = data.iloc[-1]['close']
        price_diff = abs(current_price - recent_high) / recent_high
        
        if price_diff > 0.03:  # 偏离超过3%
            return False
            
        return True

    @staticmethod
    def stable_trend_with_volume(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        趋势稳定且最近放量/波动策略
        条件1: 20日内趋势稳定性高(R方值高)
        条件2: 20日内波动率低
        条件3: 最近2天有放量或波动率放大
        输出的时得分，得分越高，说明趋势稳定且最近放量/波动的可能性越大
        """
        if len(data) < lookback_days:
            return False
            
        # 获取最近20天的数据
        recent_data = data.iloc[-lookback_days:]
        
        # 条件1: 计算趋势稳定性(R方值)
        x = np.arange(len(recent_data))
        y = recent_data['close'].values.astype(float)
        slope, intercept = np.polyfit(x, y, 1)
        y_pred = slope * x + intercept
        r_squared = 1 - np.sum((y - y_pred) ** 2) / np.sum((y - np.mean(y)) ** 2)
        
        # 条件2: 计算20日波动率
        volatility = recent_data['close'].pct_change().std()
        
        # 条件3: 检查最近2天是否有放量或波动率放大
        if len(recent_data) <= 2:
            return False
            
        recent_2d = recent_data.iloc[-2:]
        avg_volume = recent_data['volume'].iloc[:-2].mean()
        recent_volume = recent_2d['volume'].mean()
        volume_increase = recent_volume / avg_volume if avg_volume > 0 else 0
        
        recent_volatility = recent_2d['close'].pct_change().std()
        volatility_increase = recent_volatility / volatility if volatility > 0 else 0
        
        # 综合条件判断
        return (r_squared > 0.7 and 
                volatility < 0.03 and 
                (volume_increase > 1.5 or volatility_increase > 1.5))
    
    @staticmethod
    def double_bottom(data: pd.DataFrame, min_days: int = 40) -> bool:
        """
        双底(W底)突破策略
        条件1: 40日内形成两个明显低点，价格差在±3%以内
        条件2: 第二个低点成交量较第一个缩小30%以上
        条件3: 突破颈线时放量(成交量>1.5倍5日均量)
        """
        if len(data) < min_days:
            return False
            
        # 寻找两个低点
        lows = data['low'].rolling(5,min_periods=1).min()  # 5日最低价
        min1_idx = lows.idxmin()
        min1 = lows[min1_idx]

        # 排除最近5天的数据寻找第二个低点
        min1_pos = data.index.get_loc(min1_idx)
        if min1_pos + 5 >= len(data):
            return False

        # 排除最近5天的数据寻找第二个低点
        remaining_data = data.iloc[min1_pos + 5:]
        if len(remaining_data) < 5:
            return False
            
        min2_idx = remaining_data['low'].idxmin()
        min2 = data.loc[min2_idx, 'low']
        
        # 条件1: 两个低点价格差在±3%以内
        if min1 != min2 and abs(min1 - min2) / min(min1, min2) > 0.03:
            return False
            
        # 条件2: 第二个低点成交量较第一个缩小30%以上
        vol1 = data.loc[min1_idx, 'volume']
        vol2 = data.loc[min2_idx, 'volume']
        if vol2 > vol1 * 0.7:
            return False

        # 计算颈线(两个低点之间的高点)
        neck_line = data.loc[min1_idx : min2_idx, 'high'].max()
        
        # 条件3: 突破颈线时放量
        last_bar = data.iloc[-1]
        avg_vol5 = data['volume'].rolling(5).mean().iloc[-1]
        return (last_bar['close'] > neck_line and 
                last_bar['volume'] > 1.5 * avg_vol5)

    @staticmethod
    def head_shoulder_bottom(data: pd.DataFrame, min_days: int = 60) -> bool:
        """
        头肩底突破策略
        条件1: 60日内形成左肩-头部-右肩结构
        条件2: 右肩成交量高于左肩
        条件3: 突破颈线时显著放量(成交量>2倍5日均量)
        """
        if len(data) < min_days:
            return False
            
        # 寻找头部(最低点)
        head_idx = data['low'].idxmin()
        head = data.loc[head_idx, 'low']
        
        # 左肩(头部左侧的局部高点)
        left_data = data.loc[:head_idx]
        left_shoulder_idx = left_data['high'].idxmax()
        
        # 右肩(头部右侧的局部高点)
        right_data = data.loc[head_idx:]
        right_shoulder_idx = right_data['high'].idxmax()
        
        # 条件1: 验证头肩结构
        if (left_shoulder_idx >= head_idx or 
            right_shoulder_idx <= head_idx):
            return False
            
        # 条件2: 右肩成交量高于左肩
        right_vol = data.loc[right_shoulder_idx, 'volume']
        left_vol = data.loc[left_shoulder_idx, 'volume']
        if right_vol <= left_vol:
            return False
            
        # 计算颈线(左肩和右肩高点的连线)
        neck_line = max(data.loc[left_shoulder_idx, 'high'], 
                       data.loc[right_shoulder_idx, 'high'])
        
        # 条件3: 突破颈线时放量
        last_bar = data.iloc[-1]
        avg_vol5 = data['volume'].rolling(5).mean().iloc[-1]
        return (last_bar['close'] > neck_line and 
                last_bar['volume'] > 2 * avg_vol5)

    @staticmethod
    def rounding_bottom(data: pd.DataFrame, min_days: int = 50) -> bool:
        """
        圆弧底突破策略
        条件1: 50日内形成圆弧形底部形态
        条件2: 突破时放量(成交量>1.8倍10日均量)
        条件3: MACD指标出现金叉
        """
        if len(data) < min_days:
            return False
            
        # 计算价格变化率的标准差(衡量圆弧平滑度)
        price_changes = data['close'].pct_change().std()
        if price_changes > 0.03:  # 波动过大不符合圆弧特征
            return False
            
        # 条件2: 突破时放量
        last_bar = data.iloc[-1]
        avg_vol10 = data['volume'].rolling(10).mean().iloc[-1]
        if last_bar['volume'] <= 1.8 * avg_vol10:
            return False
            
        # 条件3: MACD金叉
        exp12 = data['close'].ewm(span=12, adjust=False).mean()
        exp26 = data['close'].ewm(span=26, adjust=False).mean()
        macd = exp12 - exp26
        signal = macd.ewm(span=9, adjust=False).mean()
        
        return macd.iloc[-1] > signal.iloc[-1] and macd.iloc[-2] <= signal.iloc[-2]

    @staticmethod
    def morning_star(data: pd.DataFrame, lookback_days: int = 10) -> bool:
        """
        早晨之星策略
        条件1: 出现阴线-十字星-阳线组合
        条件2: 第三根阳线覆盖首日阴线50%以上跌幅
        条件3: 组合期间成交量放大
        """
        if len(data) < 3:
            return False
            
        # 获取最近三根K线
        day1 = data.iloc[-3]
        day2 = data.iloc[-2] 
        day3 = data.iloc[-1]
        
        # 条件1: 阴线-十字星-阳线组合
        if not (day1['close'] < day1['open'] and  # 阴线
                abs(day2['close'] - day2['open']) / day2['open'] < 0.01 and  # 十字星
                day3['close'] > day3['open']):  # 阳线
            return False
            
        # 条件2: 第三根阳线覆盖首日阴线50%以上跌幅
        day1_drop = (day1['open'] - day1['close']) / day1['open']
        day3_rise = (day3['close'] - day3['open']) / day3['open']
        if day3_rise < day1_drop * 0.5:
            return False
            
        # 条件3: 组合期间成交量放大
        avg_vol = data['volume'].iloc[:-3].mean()
        return day3['volume'] > 1.5 * avg_vol

    @staticmethod
    def v_shape_bottom(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        V形底(单针探底)策略
        条件1: 20日内出现长下影线(下影线长度是实体2倍以上)
        条件2: 出现下影线后连续3日上涨
        条件3: MACD出现底背离
        """
        if len(data) < lookback_days:
            return False
            
        # 条件1: 寻找长下影线
        has_long_lower_shadow = False
        shadow_day_idx = -1
        
        for i in range(1, lookback_days + 1):
            bar = data.iloc[-i]
            if bar['open'] <= 0 or bar['close'] <= 0:
                continue
                
            body = abs(bar['close'] - bar['open'])
            lower_shadow = bar['open'] - bar['low'] if bar['close'] > bar['open'] else bar['close'] - bar['low']
            
            if lower_shadow > body * 2:
                has_long_lower_shadow = True
                shadow_day_idx = -i
                break
                
        if not has_long_lower_shadow:
            return False
            
        # 条件2: 下影线后连续3日上涨
        if len(data) < abs(shadow_day_idx) + 3:
            return False
            
        for i in range(1, 4):
            prev_bar = data.iloc[shadow_day_idx + i - 1]
            curr_bar = data.iloc[shadow_day_idx + i]
            if curr_bar['close'] <= prev_bar['close']:
                return False
                
        # 条件3: MACD底背离
        # 计算MACD
        exp12 = data['close'].ewm(span=12, adjust=False).mean()
        exp26 = data['close'].ewm(span=26, adjust=False).mean()
        macd = exp12 - exp26
        
        # 寻找价格低点和MACD高点
        price_lows = data['low'].rolling(5).min()
        macd_highs = macd.rolling(5).max()
        
        # 检查底背离(价格创新低但MACD未创新低)
        return (price_lows.iloc[-1] < price_lows.iloc[-5] and 
                macd.iloc[-1] > macd.iloc[-5])

    @staticmethod
    def latent_bottom(data: pd.DataFrame, min_days: int = 60) -> bool:
        """
        潜伏底突破策略
        条件1: 60日内价格波动率低(标准差<3%)
        条件2: 成交量长期低迷(20日均量<历史20日均量的50%)
        条件3: 突破时显著放量(成交量>3倍20日均量)
        """
        if len(data) < min_days:
            return False
            
        # 条件1: 价格波动率低
        price_volatility = data['close'].pct_change().std()
        if price_volatility >= 0.03:
            return False
            
        # 条件2: 成交量长期低迷
        avg_vol20 = data['volume'].rolling(20).mean()
        hist_avg_vol20 = avg_vol20.mean()
        if avg_vol20.iloc[-1] >= hist_avg_vol20 * 0.5:
            return False
            
        # 条件3: 突破时显著放量
        last_bar = data.iloc[-1]
        if last_bar['volume'] <= 3 * avg_vol20.iloc[-1]:
            return False
            
        # 检查是否突破近期盘整区间
        recent_high = data['high'].rolling(20).max().iloc[-1]
        recent_low = data['low'].rolling(20).min().iloc[-1]
        return last_bar['close'] > recent_high

    @staticmethod
    def rising_flag(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        上升旗形突破策略
        条件1: 前期有明显上升趋势(10日涨幅>15%)
        条件2: 形成向下倾斜的旗形整理(5日内高点下移)
        条件3: 突破旗形上沿时放量(成交量>2倍10日均量)
        """
        if len(data) < max(lookback_days, 5):
            return False
            
        # 条件1: 前期上升趋势
        try:
            price10 = data['close'].iloc[-lookback_days]
            rise = (data['close'].iloc[-1] - price10) / price10
            if rise <= 0.15:
                return False
        except Exception as e:
            print(f"Error1 in rising_flag: {e}")
            return False
            
        # 条件2: 旗形整理(最近5日高点下移)
        try:
            highs = data['high'].iloc[-5:]
            # 确保有足够数据进行比较
            if len(highs) < 2:
                return False
            # 检查每个高点是否严格大于下一个高点
            for i in range(len(highs)-1):
                if highs.iloc[i] <= highs.iloc[i+1]:
                    return False    
        except Exception as e:
            print(f"Error2 in rising_flag: {e}")
            return False
            
        # 条件3: 突破旗形上沿时放量
        try:
            flag_high = highs.max()
            last_bar = data.iloc[-1]
            avg_vol10 = data['volume'].rolling(10).mean().iloc[-1]
            return (last_bar['close'] > flag_high and 
                    last_bar['volume'] > 2 * avg_vol10)
        except Exception as e:
            print(f"Error3 in rising_flag: {e}")
            return False

    @staticmethod
    def morning_sun(data: pd.DataFrame) -> bool:
        """
        旭日东升策略
        条件1: 前一日为阴线
        条件2: 当日高开高走(开盘价>前日收盘价，收盘价>开盘价)
        条件3: 当日收盘价超过前日开盘价
        条件4: 成交量放大(>1.5倍5日均量)
        """
        if len(data) < 2:
            return False
            
        prev_day = data.iloc[-2]
        curr_day = data.iloc[-1]
        
        # 条件1: 前一日为阴线
        if prev_day['close'] >= prev_day['open']:
            return False
            
        # 条件2: 当日高开高走
        if not (curr_day['open'] > prev_day['close'] and 
                curr_day['close'] > curr_day['open']):
            return False
            
        # 条件3: 当日收盘价超过前日开盘价
        if curr_day['close'] <= prev_day['open']:
            return False
            
        # 条件4: 成交量放大
        avg_vol5 = data['volume'].rolling(5).mean().iloc[-1]
        return curr_day['volume'] > 1.5 * avg_vol5
    
    @staticmethod
    def cup_with_handle(data: pd.DataFrame, min_days: int = 60) -> bool:
        """
        杯柄形态策略
        条件1: 60日内形成U形底部(杯部)
        条件2: 杯部右侧形成小幅回调(柄部)
        条件3: 突破柄部高点时放量(成交量>1.5倍10日均量)
        """
        if len(data) < min_days:
            return False
            
        # 条件1: 寻找U形底部
        # 计算60日最低点作为杯底
        cup_bottom = data['low'].iloc[-min_days:].min()
        cup_bottom_idx = data['low'].iloc[-min_days:].idxmin()
        cup_bottom_pos = data.index.get_loc(cup_bottom_idx)  # 获取数值位置
        
        # 确保有足够数据寻找杯部右侧
        if cup_bottom_pos + 5 >= len(data):
            return False

        # 杯部左侧高点
        left_high = data['high'].iloc[-min_days:cup_bottom_pos].max()
        # 杯部右侧高点
        right_high = data['high'].iloc[cup_bottom_pos:].max()
        
        # 杯部形态验证(右侧高点接近左侧高点)
        if abs(right_high - left_high) / left_high > 0.1:
            return False
            
        # 条件2: 寻找柄部(杯部右侧的小幅回调)
        handle_start_idx = data['high'].iloc[cup_bottom_pos:].idxmax()
        handle_data = data.loc[handle_start_idx:]
        
        if len(handle_data) < 5:
            return False
            
        # 柄部应有小幅回调
        handle_low = handle_data['low'].min()
        if (right_high - handle_low) / right_high > 0.08:
            return False
            
        # 条件3: 突破柄部高点时放量
        last_bar = data.iloc[-1]
        avg_vol10 = data['volume'].rolling(10).mean().iloc[-1]
        return (last_bar['close'] > right_high and 
                last_bar['volume'] > 1.5 * avg_vol10)

    @staticmethod
    def triangle_breakout(data: pd.DataFrame, min_days: int = 30) -> bool:
        """
        三角形突破策略
        条件1: 30日内形成收敛三角形(高点和低点分别收敛)
        条件2: 突破三角形上沿时放量(成交量>1.8倍10日均量)
        条件3: MACD指标金叉或即将金叉
        """
        if len(data) < min_days:
            return False
            
        # 条件1: 验证三角形形态
        highs = data['high'].iloc[-min_days:]
        lows = data['low'].iloc[-min_days:]
        
        # 计算高点和低点的趋势线斜率
        x = np.arange(len(highs))
        high_slope = np.polyfit(x, highs, 1)[0]
        low_slope = np.polyfit(x, lows, 1)[0]
        
        # 高点应下降，低点应上升(收敛三角形)
        if not (high_slope < -0.001 and low_slope > 0.001):
            return False
            
        # 条件2: 突破三角形上沿时放量
        resistance = highs.max()
        last_bar = data.iloc[-1]
        avg_vol10 = data['volume'].rolling(10).mean().iloc[-1]
        
        if not (last_bar['close'] > resistance and 
                last_bar['volume'] > 1.8 * avg_vol10):
            return False
            
        # 条件3: MACD指标金叉或即将金叉
        exp12 = data['close'].ewm(span=12, adjust=False).mean()
        exp26 = data['close'].ewm(span=26, adjust=False).mean()
        macd = exp12 - exp26
        signal = macd.ewm(span=9, adjust=False).mean()
        
        return (macd.iloc[-1] > signal.iloc[-1] or 
                abs(macd.iloc[-1] - signal.iloc[-1]) / signal.iloc[-1] < 0.02)

    @staticmethod
    def volume_breakout(data: pd.DataFrame, min_days: int = 20) -> bool:
        """
        放量突破策略
        条件1: 20日内价格波动率逐渐降低
        条件2: 突破时成交量突然放大(>3倍20日均量)
        条件3: 突破价格创20日新高
        """
        if len(data) < min_days:
            return False
            
        # 条件1: 价格波动率逐渐降低
        volatility = data['close'].pct_change().rolling(5).std()
        if not all(volatility.iloc[-5:-1] > volatility.iloc[-1]):
            return False
            
        # 条件2: 成交量突然放大
        avg_vol20 = data['volume'].rolling(min_days).mean().iloc[-1]
        last_bar = data.iloc[-1]
        if last_bar['volume'] <= 3 * avg_vol20:
            return False
            
        # 条件3: 突破价格创20日新高
        return last_bar['close'] == data['high'].iloc[-min_days:].max()

    @staticmethod
    def rectangle_breakout(data: pd.DataFrame, min_days: int = 40) -> bool:
        """
        矩形整理突破策略
        条件1: 40日内形成矩形整理形态(高低点波动<8%)
        条件2: 突破箱体上沿时放量(成交量>2倍20日均量)
        条件3: RSI指标>50
        """
        if len(data) < min_days:
            return False
            
        # 条件1: 验证矩形形态
        highs = data['high'].iloc[-min_days:]
        lows = data['low'].iloc[-min_days:]
        
        price_range = highs.max() - lows.min()
        if price_range / lows.min() > 0.08:
            return False
            
        # 条件2: 突破箱体上沿时放量
        resistance = highs.max()
        last_bar = data.iloc[-1]
        avg_vol20 = data['volume'].rolling(20).mean().iloc[-1]
        
        if not (last_bar['close'] > resistance and 
                last_bar['volume'] > 2 * avg_vol20):
            return False
            
        # 条件3: RSI指标>50
        delta = data['close'].diff()
        gain = delta.where(delta > 0, 0)
        loss = -delta.where(delta < 0, 0)
        avg_gain = gain.rolling(14).mean()
        avg_loss = loss.rolling(14).mean()
        rs = avg_gain / avg_loss
        rsi = 100 - (100 / (1 + rs))
        
        return rsi.iloc[-1] > 50
    
    @staticmethod
    def low_breakout_strategy(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        低位突破策略
        条件1: 前20天横盘(价格波动范围<15%)
        条件2: 前20天成交量波动较小(标准差/均值<0.3)
        条件3: 前一天涨停且放量大于3倍前20天平均成交量
        """
        if len(data) < lookback_days + 1:
            return False
            
        # 获取前20天数据(不包括最新一天)
        prev_data = data.iloc[-lookback_days-1:-1]
        
        # 条件1: 价格波动范围<5%
        high_20 = prev_data['high'].max()
        low_20 = prev_data['low'].min()
        price_range = (high_20 - low_20) / low_20
        if price_range >= 0.15:
            return False
            
        # 条件2: 成交量波动较小(标准差/均值<0.3)
        vol_mean = prev_data['volume'].mean()
        vol_std = prev_data['volume'].std()
        if vol_mean == 0 or vol_std / vol_mean >= 0.3:
            return False
            
        # 条件3: 前一天涨停
        last_day = data.iloc[-1]  # 前一天数据
        if last_day['close'] == last_day['upper_px'] and last_day['volume'] >= 3 * vol_mean:
            return True
            
        return False

    @staticmethod
    def high_breakout_strategy(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        高位突破选股策略
        条件1: 突破20日新高
        条件2: 前一天为涨停，今天为阴线且低于昨天收盘价
        参数:
            data: 包含股票历史数据的DataFrame，需包含open,close,high,upper_px等字段
            lookback_days: 回溯天数，默认为20
        返回:
            bool: 是否符合选股条件
        """
        if len(data) < lookback_days + 2:  # 需要至少22天数据(20天+涨停前一天+今天)
            return False
            
        # 条件1: 突破20日新高
        # 获取前20天(不包括最近2天)的最高价
        prev_20d_high = data['high'].iloc[-lookback_days-2:-2].max()
        current_close = data['close'].iloc[-1]
        if current_close <= prev_20d_high:
            return False
        
        # 条件2: 前一天为涨停且今天为阴线
        # 前一天数据(倒数第二行)
        prev_day = data.iloc[-2]
        # 今天数据(最后一行)
        today = data.iloc[-1]
        
        # 判断前一天是否涨停
        is_prev_day_limit_up = prev_day['close'] == prev_day['upper_px']
        # 判断今天是否为阴线(收盘价低于开盘价)
        is_today_down = today['close'] < today['open'] and today['open'] < prev_day['close']
        
        return is_prev_day_limit_up and is_today_down
    
    @staticmethod
    def low_volume_rise_with_stable_baseline(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        缩量拉涨策略(前期波动平稳)
        条件1: 最近5日上涨但成交量缩小(相比5日均量)
        条件2: 前期(20日)波动率低(价格标准差/均值<15%)
        条件3: 前期(20日)趋势稳定(R方值>0.7)
        """
        if len(data) < lookback_days:
            return False
            
        # 条件1: 最近5日上涨但缩量
        recent_5d = data.iloc[-5:]
        if len(recent_5d) < 5:
            return False
            
        # 检查上涨趋势
        price_rise = (recent_5d['close'].iloc[-1] - recent_5d['close'].iloc[0]) / recent_5d['close'].iloc[0]
        if price_rise <= 0:
            return False
            
        # 检查成交量缩小
        avg_vol5 = data['volume'].rolling(5).mean().iloc[-6]  # 5天前的5日均量
        recent_vol = recent_5d['volume'].mean()
        if recent_vol >= avg_vol5 * 0.9:  # 成交量未缩小10%以上
            return False
            
        # 条件2: 前期波动率低
        baseline_data = data.iloc[-lookback_days:-5]
        price_std = baseline_data['close'].std()
        price_mean = baseline_data['close'].mean()
        if price_std / price_mean >= 0.15:  # 波动率>15%
            return False
            
        # 条件3: 前期趋势稳定(R方值>0.7)
        x = np.arange(len(baseline_data))
        y = baseline_data['close'].values.astype(float)
        slope, intercept = np.polyfit(x, y, 1)
        y_pred = slope * x + intercept
        r_squared = 1 - np.sum((y - y_pred) ** 2) / np.sum((y - np.mean(y)) ** 2)
        
        return r_squared > 0.7
    
    @staticmethod
    def ma_crossover(data: pd.DataFrame, short_period: int = 5, long_period: int = 20) -> bool:
        """
        均线金叉策略(5日线上穿20日线)
        条件1: 计算5日和20日均线
        条件2: 今日5日均线 > 20日均线
        条件3: 昨日5日均线 <= 20日均线
        条件4: 成交量放大(今日成交量>5日均量)
        """
        if len(data) < long_period + 1:  # 需要足够的数据计算均线
            return False
            
        # 计算均线
        ma5 = data['close'].rolling(short_period).mean()
        ma20 = data['close'].rolling(long_period).mean()
        
        # 条件2: 今日5日线 > 10日线
        if ma5.iloc[-1] <= ma20.iloc[-1]:
            return False
            
        # 条件3: 昨日5日线 <= 10日线
        if ma5.iloc[-2] > ma20.iloc[-2]:
            return False
            
        # 条件4: 成交量放大
        avg_vol5 = data['volume'].rolling(5).mean().iloc[-1]
        if data['volume'].iloc[-1] <= avg_vol5:
            return False
            
        return True

    @staticmethod
    def limit_up_then_down(data: pd.DataFrame, lookback_days: int = 10) -> bool:
        """
        涨停后收阴策略
        条件1: 最近10日内有涨停
        条件2: 涨停后一天收阴(收盘价<开盘价)
        条件3: 溢价不超过1%(收盘价与开盘价差不超过1%)
        
        参数:
            data: 股票数据DataFrame
            lookback_days: 回溯天数(默认10天)
        返回:
            bool: 是否符合策略条件
        """
        if len(data) < lookback_days + 1:  # 需要至少11天数据(10天+涨停当天)
            return False
            
        # 寻找最近一次涨停
        for i in range(1, lookback_days + 1):
            # 涨停条件: 最近的一个触发涨停的,这天量是前面的倍数
            if data.iloc[-i]['high'] == data.iloc[-i]['upper_px']:
                # 检查第二天数据是否存在
                if len(data) <= -i + 1:
                    return False

                # 今天的量是前面1.5倍

                next_day = data.iloc[-i + 1]
                # 条件2: 收阴(收盘价<开盘价)
                if next_day['close'] >= next_day['open']:
                    return False
                    
                # 条件3: 溢价不超过1%
                premium = (next_day['high'] - next_day['pre_close']) / next_day['pre_close']
                if premium <= 0.01:
                    logger.debug(f"涨停后收阴策略选中1: {data.iloc[-i]['security_id']}, 日期: {data.iloc[-i]['trade_date']}, 涨停价: {data.iloc[-i]['upper_px']}")
                    logger.debug(f"涨停后收阴策略选中2: {next_day['security_id']}, 日期: {next_day['trade_date']}, 收盘价: {next_day['close']}")
                    return True
                else:
                    return False

                    
        return False

    @staticmethod
    def rising_close_5days(data: pd.DataFrame, lookback_days: int = 5) -> bool:
        """
        判断股票是否连续5天收盘价递增
        参数:
            data: 包含历史交易数据的DataFrame
        返回:
            bool: 是否满足连续5天收盘价递增
        """
        if len(data) < lookback_days:
            return False
        
        # 获取最近5天的收盘价
        closes = data['close'].tail(lookback_days).values
        
        first_day_close = closes[0]
        last_day_close = closes[-1]
        zf = (last_day_close - first_day_close) / first_day_close
        if zf < 0.05:
            return False

        # 检查是否每天收盘价都比前一天高
        for i in range(1, lookback_days):
            if closes[i] <= closes[i-1]:
                return False
        
        return True

    @staticmethod
    def lowest_in_60days(data: pd.DataFrame) -> bool:
        # 检查数据是否至少有60天
        if len(data) < 60:
            return False
        # 获取最近60天的数据
        recent_data = data.tail(60)
        # 当前收盘价是否为60天内最低
        current_close = recent_data.iloc[-1]['close']
        lowest_close = recent_data['close'].min()
        
        # 计算60日内最高价和最低价
        highest_high = recent_data['high'].max()
        lowest_low = recent_data['low'].min()
        
        # 计算振幅 (最高价 - 最低价) / 最低价
        amplitude = (highest_high - lowest_low) / lowest_low
        
        # 检查两个条件：收盘价最低且振幅大于30%
        return current_close == lowest_close and amplitude > 0.3

    @staticmethod
    def three_yin_not_break_yang(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        三阴不破阳策略
        条件1: 最近20日内有涨停板
        条件2: 涨停后出现连续3根阴线
        条件3: 3根阴线都是缩量的(成交量小于涨停日成交量的80%)
        条件4: 3根阴线的最低价未破涨停日的最低价
        条件5: 第4天开始有反弹迹象(可选)
        条件6: 涨停日成交量放大(可选，增强筛选)
        
        参数:
            data: 股票数据DataFrame
            lookback_days: 回溯天数(默认20天)
        返回:
            bool: 是否符合策略条件
        """
        if len(data) < lookback_days + 3:  # 需要至少23天数据(20天+涨停日+3天阴线)
            return False
            
        # 寻找最近一次涨停
        limit_up_idx = -1
        limit_up_day = None
        
        for i in range(1, lookback_days + 1):
            if data.iloc[-i]['close'] == data.iloc[-i]['upper_px']:  # 涨停
                limit_up_idx = -i
                limit_up_day = data.iloc[-i]
                break
                
        if limit_up_idx == -1 or limit_up_day is None:
            return False
            
        # 检查涨停后是否有足够的数据
        if len(data) < abs(limit_up_idx) + 3:
            return False
            
        # 获取涨停日数据
        limit_up_volume = limit_up_day['volume']
        limit_up_low = limit_up_day['low']
        limit_up_close = limit_up_day['close']
        
        # 条件6: 检查涨停日成交量是否放大(相对于前5日均量)
        if abs(limit_up_idx) >= 6:
            avg_vol_5 = data['volume'].iloc[limit_up_idx-5:limit_up_idx].mean()
            if limit_up_volume < avg_vol_5 * 1.5:  # 涨停日成交量小于前5日均量的1.5倍
                return False
        
        # 检查涨停后连续3根阴线
        three_yin_days = []
        for i in range(1, 4):  # 涨停后1-3天
            day_idx = limit_up_idx + i
            if day_idx >= 0:  # 超出数据范围
                return False
                
            day_data = data.iloc[day_idx]
            
            # 检查是否为阴线(收盘价 < 开盘价)
            if day_data['close'] >= day_data['open']:
                return False
                
            # 检查是否缩量(成交量 < 涨停日成交量的80%)
            if day_data['volume'] >= limit_up_volume * 0.8:
                return False
                
            # 检查最低价是否破涨停日最低价
            if day_data['low'] < limit_up_low:
                return False
                
            # 额外检查：阴线跌幅不能过大(单日跌幅不超过5%)
            day_drop = (day_data['open'] - day_data['close']) / day_data['open']
            if day_drop > 0.05:
                return False
                
            three_yin_days.append(day_data)
            
        # 验证3根阴线都满足条件
        if len(three_yin_days) != 3:
            return False
            
        # 检查3根阴线的累计跌幅(不能超过涨停日涨幅的50%)
        total_drop = (three_yin_days[0]['open'] - three_yin_days[2]['close']) / three_yin_days[0]['open']
        limit_up_rise = (limit_up_close - limit_up_day['open']) / limit_up_day['open']
        if total_drop > limit_up_rise * 0.5:
            return False
            
        # 可选：检查第4天是否有反弹迹象
        if len(data) >= abs(limit_up_idx) + 4:
            day4_idx = limit_up_idx + 4
            if day4_idx < 0:
                day4_data = data.iloc[day4_idx]
                # 第4天收盘价高于第3天收盘价
                if day4_data['close'] > three_yin_days[2]['close']:
                    return True
                    
        # 即使第4天没有反弹，只要满足前5个条件也算符合策略
        return True

    @staticmethod
    def limit_up_rubbing_line(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        涨停揉搓线策略
        条件1: 最近20日内有涨停板
        条件2: 涨停后次日冲高回落有长上影线，收阴线，缩量
        条件3: 再下一交易日长下影线收阳，缩量
        条件4: 涨停日成交量放大(可选，增强筛选)
        
        参数:
            data: 股票数据DataFrame
            lookback_days: 回溯天数(默认20天)
        返回:
            bool: 是否符合策略条件
        """
        if len(data) < lookback_days + 3:  # 需要至少23天数据(20天+涨停日+2天揉搓线)
            return False
            
        # 寻找最近一次涨停
        limit_up_idx = -1
        limit_up_day = None
        
        for i in range(1, lookback_days + 1):
            if data.iloc[-i]['close'] == data.iloc[-i]['upper_px']:  # 涨停
                limit_up_idx = -i
                limit_up_day = data.iloc[-i]
                break
                
        if limit_up_idx == -1 or limit_up_day is None:
            return False
            
        # 检查涨停后是否有足够的数据
        if len(data) < abs(limit_up_idx) + 2:
            return False
            
        # 获取涨停日数据
        limit_up_volume = limit_up_day['volume']
        limit_up_close = limit_up_day['close']
        
        # 添加涨停日期调试日志
        logger.debug(f"涨停揉搓线策略 - 涨停日期: {limit_up_day['trade_date']}, 涨停价: {limit_up_close * 0.0001:.3f}")
        
        # 条件4: 检查涨停日成交量是否放大(相对于前5日均量)
        if abs(limit_up_idx) >= 6:
            avg_vol_5 = data['volume'].iloc[limit_up_idx-5:limit_up_idx].mean()
            if limit_up_volume < avg_vol_5 * 1.3:  # 涨停日成交量小于前5日均量的1.3倍
                return False
        
        # 涨停后次日(第1天) - 冲高回落有长上影线，收阴线，缩量
        day1_idx = limit_up_idx + 1
        if day1_idx >= 0:  # 超出数据范围
            return False
            
        day1_data = data.iloc[day1_idx]
        
        # 检查是否为阴线(收盘价 < 开盘价)
        if day1_data['close'] >= day1_data['open']:
            return False
            
        # 检查是否有长上影线(上影线长度 > 实体长度的1.5倍)
        body_length = abs(day1_data['close'] - day1_data['open'])
        upper_shadow = day1_data['high'] - max(day1_data['open'], day1_data['close'])
        if upper_shadow <= body_length * 1.1:
            return False
            
        # 检查是否缩量(成交量 < 涨停日成交量的70%)
        if day1_data['volume'] >= limit_up_volume * 0.7:
            return False
            
        # 检查是否冲高回落(最高价 > 涨停价)
        if day1_data['high'] <= limit_up_close:
            return False
            
        # 涨停后第2天 - 长下影线收阳，缩量
        day2_idx = limit_up_idx + 2
        if day2_idx >= 0:  # 超出数据范围
            return False
            
        day2_data = data.iloc[day2_idx]
        
        # 检查是否为阳线(收盘价 > 开盘价)
        if day2_data['close'] <= day2_data['open']:
            return False
            
        # 检查是否有长下影线(下影线长度 > 实体长度的1.5倍)
        body_length2 = abs(day2_data['close'] - day2_data['open'])
        lower_shadow = min(day2_data['open'], day2_data['close']) - day2_data['low']
        if lower_shadow <= body_length2 * 1.1:
            return False
            
        # 检查是否缩量(成交量 < 涨停日成交量的70%)
        if day2_data['volume'] >= limit_up_volume * 0.7:
            return False
            
        # 检查第2天收盘价是否高于第1天收盘价(反弹迹象)
        if day2_data['close'] <= day1_data['close']:
            return False
            
        # 额外检查：第2天收盘价不能过高(不超过涨停价的1.05倍)
        if day2_data['close'] > limit_up_close * 1.05:
            return False
            
        return True

    @staticmethod
    def limit_up_shrinking_volume(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        涨停缩倍量策略
        条件1: 最近20日内有涨停板
        条件2: 涨停后次日收阴线缩量(成交量 < 涨停日成交量的50%)
        条件3: 后续3个交易日内出现放量收阳(成交量 > 涨停日成交量的80%且收盘价 > 开盘价)
        
        参数:
            data: 股票数据DataFrame
            lookback_days: 回溯天数(默认20天)
        返回:
            bool: 是否符合策略条件
        """
        if len(data) < lookback_days + 4:  # 需要至少24天数据(20天+涨停日+次日+3天观察期)
            return False
            
        # 寻找最近一次涨停
        limit_up_idx = -1
        limit_up_day = None
        
        for i in range(1, lookback_days + 1):
            if data.iloc[-i]['close'] == data.iloc[-i]['upper_px']:  # 涨停
                limit_up_idx = -i
                limit_up_day = data.iloc[-i]
                break
                
        if limit_up_idx == -1 or limit_up_day is None:
            return False
            
        # 检查涨停后是否有足够的数据
        if len(data) < abs(limit_up_idx) + 4:  # 涨停日+次日+3天观察期
            return False
            
        # 获取涨停日数据
        limit_up_volume = limit_up_day['volume']
        limit_up_close = limit_up_day['close']
        
        # 添加涨停日期调试日志
        logger.debug(f"涨停缩倍量策略 - 涨停日期: {limit_up_day['trade_date']}, 涨停价: {limit_up_close * 0.0001:.3f}")
        
        # 涨停后次日(第1天) - 收阴线缩量
        day1_idx = limit_up_idx + 1
        if day1_idx >= 0:  # 超出数据范围
            return False
            
        day1_data = data.iloc[day1_idx]
        
        # 检查是否为阴线(收盘价 < 开盘价)
        if day1_data['close'] >= day1_data['open']:
            return False
            
        # 检查是否缩量(成交量 < 涨停日成交量的50%)
        if day1_data['volume'] >= limit_up_volume * 0.5:
            return False
                    
        return True

    @staticmethod
    def calculate_kdj(data: pd.DataFrame, n: int = 9, m1: int = 3, m2: int = 3) -> tuple:
        """
        计算KDJ指标
        参数:
            data: 股票数据DataFrame
            n: RSV计算周期，默认9
            m1: K值平滑系数，默认3
            m2: D值平滑系数，默认3
        返回:
            (K, D, J): K、D、J值序列
        """
        high = data['high']
        low = data['low']
        close = data['close']
        
        # 计算RSV
        low_n = low.rolling(n).min()
        high_n = high.rolling(n).max()
        rsv = (close - low_n) / (high_n - low_n) * 100
        
        # 计算K值
        k = pd.Series(index=data.index, dtype=float)
        k.iloc[0] = 50.0
        for i in range(1, len(data)):
            k.iloc[i] = (m1 - 1) / m1 * k.iloc[i-1] + 1 / m1 * rsv.iloc[i]
        
        # 计算D值
        d = pd.Series(index=data.index, dtype=float)
        d.iloc[0] = 50.0
        for i in range(1, len(data)):
            d.iloc[i] = (m2 - 1) / m2 * d.iloc[i-1] + 1 / m2 * k.iloc[i]
        
        # 计算J值
        j = 3 * k - 2 * d
        
        return k, d, j
    
    @staticmethod
    def calculate_rsi(data: pd.DataFrame, period: int = 14) -> pd.Series:
        """
        计算RSI指标
        参数:
            data: 股票数据DataFrame
            period: RSI计算周期，默认14
        返回:
            RSI值序列
        """
        close = data['close']
        delta = close.diff()
        
        gain = delta.where(delta > 0, 0)
        loss = -delta.where(delta < 0, 0)
        
        avg_gain = gain.rolling(period).mean()
        avg_loss = loss.rolling(period).mean()
        
        rs = avg_gain / avg_loss
        rsi = 100 - (100 / (1 + rs))
        
        return rsi
    
    @staticmethod
    def calculate_max_drawdown(data: pd.DataFrame, period: int = 20) -> float:
        """
        计算指定期间内的最大回撤
        参数:
            data: 股票数据DataFrame
            period: 计算周期，默认20天
        返回:
            最大回撤百分比
        """
        if len(data) < period:
            return 0.0
        
        recent_data = data.tail(period)
        high_prices = recent_data['high']
        low_prices = recent_data['low']
        
        # 计算期间内的最高价和最低价
        period_high = high_prices.max()
        period_low = low_prices.min()
        
        # 计算最大回撤
        max_drawdown = (period_high - period_low) / period_high * 100
        
        return max_drawdown

    @staticmethod
    def oversold_rebound_technical_turning_point(data: pd.DataFrame, lookback_days: int = 20) -> bool:
        """
        超跌反弹+技术拐点策略
        条件1: 近20日最大回撤>15%
        条件2: KDJ金叉(今日K线上穿D线，昨日K线<=D线) 或者 RSI从低位反弹(今日RSI>30且比昨日RSI高) 或者 收复5日均线(今日收盘价>5日均线)
        
        参数:
            data: 股票数据DataFrame
            lookback_days: 回撤计算天数，默认20天
        返回:
            bool: 是否符合策略条件
        """
        if len(data) < max(lookback_days, 30):  # 需要足够的数据计算指标
            return False
        
        # 条件1: 近20日最大回撤>15%
        max_drawdown = StockSelector.calculate_max_drawdown(data, lookback_days)
        if max_drawdown <= 15.0:
            return False
        
        # 计算技术指标
        k, d, j = StockSelector.calculate_kdj(data)
        rsi = StockSelector.calculate_rsi(data)
        ma5 = data['close'].rolling(5).mean()
        
        # 检查是否有足够的数据计算指标
        if len(k) < 2 or len(rsi) < 2 or len(ma5) < 1:
            return False
        
        # 条件2: KDJ金叉(今日K线上穿D线，昨日K线<=D线)
        today_k = k.iloc[-1]
        today_d = d.iloc[-1]
        yesterday_k = k.iloc[-2]
        yesterday_d = d.iloc[-2]
        
        kdj_golden_cross = (today_k > today_d and yesterday_k <= yesterday_d)
        
        # 条件3: RSI从低位反弹(今日RSI>30且比昨日RSI高)
        today_rsi = rsi.iloc[-1]
        yesterday_rsi = rsi.iloc[-2]
        
        rsi_rebound = (today_rsi > 30 and today_rsi > yesterday_rsi)
        
        # 条件4: 收复5日均线(今日收盘价>5日均线)
        today_close = data['close'].iloc[-1]
        today_ma5 = ma5.iloc[-1]
        
        ma5_recovery = (today_close > today_ma5)
        
        # 三个技术条件满足其中一个即可（逻辑或）
        return kdj_golden_cross or rsi_rebound or ma5_recovery


    @staticmethod
    def positive_net_and_big_net_inflow_3days(data: pd.DataFrame, days: int = 3) -> bool:
        """
        最近3日净流入和主力净流入都为正的股票策略
        条件: 最近3个交易日的netamt和bignetamt字段都为正数
        """
        if len(data) < days:
            return False
            
        # 检查是否有必要的字段
        if 'netamt' not in data.columns:
            logger.warning("数据中缺少netamt字段")
            return False
            
        if 'bignetamt' not in data.columns:
            logger.warning("数据中缺少bignetamt字段")
            return False
            
        # 获取最近3个交易日的净流入和主力净流入数据
        recent_netamt = data['netamt'].tail(days)
        recent_bignetamt = data['bignetamt'].tail(days)
        
        # 检查是否所有值都为正数
        if len(recent_netamt) < days or len(recent_bignetamt) < days:
            return False
            
        netamt_positive = all(recent_netamt > 0)
        bignetamt_positive = all(recent_bignetamt > 0)
        
        # 两个条件都必须满足
        both_positive = netamt_positive and bignetamt_positive
        
        if both_positive:
            logger.info(f"股票满足最近{days}日净流入和主力净流入都为正的条件")
            logger.info(f"  净流入额: {recent_netamt.values}")
            logger.info(f"  主力净流入额: {recent_bignetamt.values}")
            
        return both_positive

    @staticmethod
    def limit_up_pullback_restart_strategy(data: pd.DataFrame, lookback_days: int = 100) -> bool:
        """
        涨停回调再启动策略 (通达信公式转换)
        条件1: 最近60日内存在至少1次涨停（收盘价 ≥ 前日收盘 * 1.098 ≈ 9.8%涨幅）
        条件2: 最近100日内涨停次数 ≥ 2次
        条件3: 当日最高价涨幅 > 0.07%（相对前日收盘价）
        条件4: 当日最高价创2日内新高 且 上次涨停发生在20日内
        #条件5: 排除创业板(300/301)、科创板(688)、新三板(8开头)
        条件6: 当日收阳线（收盘 > 昨收）
        条件7: 当日涨幅 < 15%（相对前两日收盘）
        
        参数:
            data: 股票数据DataFrame
            lookback_days: 回溯天数(默认100天)
        返回:
            bool: 是否符合策略条件
        """
        if len(data) < lookback_days:
            return False
            
        # 条件1: 最近60日内存在至少1次涨停
        recent_60d = data.iloc[-60:]
        has_limit_up_60d = False
        for i in range(len(recent_60d)):
            if i == 0:  # 第一天没有前日数据
                continue
            current_close = recent_60d.iloc[i]['close']
            prev_close = recent_60d.iloc[i-1]['close']
            if prev_close > 0 and current_close >= prev_close * 1.098:
                has_limit_up_60d = True
                break
                
        if not has_limit_up_60d:
            return False
            
        # 条件2: 最近100日内涨停次数 ≥ 2次
        recent_100d = data.iloc[-lookback_days:]
        limit_up_count = 0
        for i in range(len(recent_100d)):
            if i == 0:  # 第一天没有前日数据
                continue
            current_close = recent_100d.iloc[i]['close']
            prev_close = recent_100d.iloc[i-1]['close']
            if prev_close > 0 and current_close >= prev_close * 1.098:
                limit_up_count += 1
                
        if limit_up_count < 2:
            return False
            
        # 条件3: 当日最高价涨幅 > 0.07%（相对前日收盘价）
        current_high = data.iloc[-1]['high']
        prev_close = data.iloc[-2]['close']
        if prev_close <= 0:
            return False
        high_rise = (current_high - prev_close) / prev_close
        if high_rise <= 0.0007:
            return False
            
        # 条件4: 当日最高价创2日内新高 且 上次涨停发生在20日内
        # 计算距离上次涨停的周期数
        last_limit_up_days = float('inf')
        for i in range(1, min(21, len(data))):  # 最近20天
            if i >= len(data):
                break
            current_close = data.iloc[-i]['close']
            prev_close = data.iloc[-i-1]['close'] if i+1 < len(data) else 0
            if prev_close > 0 and current_close >= prev_close * 1.098:
                last_limit_up_days = i
                break
                
        if last_limit_up_days >= 20:
            return False
            
        # 检查当日最高价是否创2日内新高
        if len(data) < 2:
            return False
        recent_2d_high = data['high'].iloc[-2:].max()
        if current_high < recent_2d_high:
            return False
            
        # 条件5: 排除创业板(300/301)、科创板(688)、新三板(8开头)
        # security_id = str(data.iloc[-1]['security_id'])
        # if (security_id.startswith('300') or 
        #     security_id.startswith('301') or 
        #     security_id.startswith('688') or 
        #     security_id.startswith('8')):
        #     return False
            
        # 条件6: 当日收阳线（收盘 > 昨收）
        current_close = data.iloc[-1]['close']
        if current_close <= prev_close:
            return False
            
        # 条件7: 当日涨幅 < 15%（相对前两日收盘）
        if len(data) < 3:
            return False
        two_days_ago_close = data.iloc[-3]['close']
        if two_days_ago_close <= 0:
            return False
        two_day_rise = (current_close - two_days_ago_close) / two_days_ago_close
        if two_day_rise >= 0.15:
            return False
            
        return True

if __name__ == '__main__':
    
    logger.add(
        "stock_select_{time:YYYYMMDD}.log",
        rotation="1 day",
        retention="7 days",
        encoding="utf-8",
        format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <level>{message}</level>"
    )

    # 初始化选股器
    selector = StockSelector()

    # 加载数据
    data = selector.load_orderbook_data("data/hsnapshot_*.csv")
    
    # 定义要执行的选股策略列表 [策略名称, 中文描述]
    strategy_list = [
        ['breakout_platform', '平台突破策略'],
        ['airfield_strategy', '停机坪策略'],
        ['volume_limit_down', '放量跌停策略'],
#        ['no_large_drawdown', '无大幅回撤策略'],
        ['flag_pattern', '旗形突破策略'],
        ['four_up_three_down', '四连板两连跌策略'],
        ['double_bottom', '双底(W底)突破策略'],
        ['head_shoulder_bottom', '头肩底突破策略'],
        ['rounding_bottom', '圆弧底突破策略'],
        ['morning_star', '早晨之星策略'],
        ['v_shape_bottom', 'V形底策略'],
        ['latent_bottom', '潜伏底策略'],
        ['rising_flag', '上升旗形策略'],
        ['morning_sun', '旭日东升策略'], #选出的数据趋势明显
        ['cup_with_handle', '杯柄形态策略'],
        ['triangle_breakout', '三角形突破策略'], 
        ['volume_breakout', '放量突破策略'],
        ['rectangle_breakout', '矩形整理突破策略'],
        ['low_breakout_strategy', '低位突破策略'],
        ['high_breakout_strategy', '高位突破策略'],
        ['low_volume_rise_with_stable_baseline', '缩量拉涨策略(前期波动平稳)'],
        ['ma_crossover', '均线金叉策略(5日线上穿20日线)'],
        ['limit_up_then_down', '涨停后收阴策略'], #再次涨停或突破前期涨停位买入
        ['rising_close_5days', '趋势向上'],
        ['lowest_in_60days', '最近60日最低位'],
        ['three_yin_not_break_yang', '回调：三阴不破阳策略'], #涨停后三根缩量阴线不破涨停日最低价
        ['limit_up_rubbing_line', '回调：涨停揉搓线策略'], #涨停后次日长上影线阴线缩量，再下日长下影线阳线缩量
        ['limit_up_shrinking_volume', '回调：涨停缩倍量策略'], #涨停后次日收阴线缩量，后续3个交易日内放量收阳
        ['oversold_rebound_technical_turning_point', '超跌反弹+技术拐点策略'], #近20日最大回撤>15%,KDJ金叉,RSI从低位反弹,收复5日均线
        ['positive_net_and_big_net_inflow_3days', '最近3日净流入和主力净流入都为正策略'], #最近3日净流入和主力净流入都为正
        ['limit_up_pullback_restart_strategy', '涨停回调再启动策略'] #通达信公式转换：涨停回调再启动策略
    ]
    
    results = []
    # 遍历执行所有选股策略
    for strategy,strategy_name in strategy_list:
        try:
            logger.info(f"\n{strategy_name}策略选中的股票:")
            selected_stocks = selector.apply_strategy_to_all(data, strategy)
            
            if not selected_stocks.empty:
                for _, row in selected_stocks.iterrows():
                    if strategy == 'three_yin_not_break_yang':
                        logger.info(f"证券代码: {row['security_id']}, 日期: {row['trade_date']}, 收盘价: {row['close'] * 0.0001:.3f}")
                        logger.info(f"  涨停日期: {row['limit_up_date']}, 涨停价: {row['limit_up_price']:.3f}")
                        logger.info(f"  累计跌幅: {row['total_drop_pct']:.2f}%, 涨停涨幅: {row['limit_up_rise_pct']:.2f}%")
                        
                        result_dict = {
                            '策略名称': strategy_name,
                            '证券代码': row['security_id'],
                            '证券名称': row['security_name'],
                            '日期': row['trade_date'],
                            '收盘价': row['close'] * 0.0001,
                            '涨停次数': row['upper_times'],
                            '涨停日期': row['limit_up_date'],
                            '涨停价': row['limit_up_price'],
                            '累计跌幅': row['total_drop_pct'],
                            '涨停涨幅': row['limit_up_rise_pct']
                        }
                    elif strategy == 'limit_up_then_down':
                        logger.info(f"证券代码: {row['security_id']}, 日期: {row['trade_date']}, 收盘价: {row['close'] * 0.0001:.3f}")
                        logger.info(f"  涨停日期: {row['limit_up_date']}, 涨停价: {row['limit_up_price']:.3f}")
                        logger.info(f"  次日开盘价: {row['next_day_open']:.3f}, 次日收盘价: {row['next_day_close']:.3f}")
                        
                        result_dict = {
                            '策略名称': strategy_name,
                            '证券代码': row['security_id'],
                            '证券名称': row['security_name'],
                            '日期': row['trade_date'],
                            '收盘价': row['close'] * 0.0001,
                            '涨停次数': row['upper_times'],
                            '涨停日期': row['limit_up_date'],
                            '涨停价': row['limit_up_price'],
                            '次日开盘价': row['next_day_open'],
                            '次日收盘价': row['next_day_close']
                        }
                    elif strategy == 'limit_up_rubbing_line':
                        logger.info(f"证券代码: {row['security_id']}, 日期: {row['trade_date']}, 收盘价: {row['close'] * 0.0001:.3f}")
                        logger.info(f"  涨停日期: {row['limit_up_date']}, 涨停价: {row['limit_up_price']:.3f}")
                        logger.info(f"  第1天上影线: {row['day1_upper_shadow_pct']:.2f}%, 第2天下影线: {row['day2_lower_shadow_pct']:.2f}%")
                        logger.info(f"  第1天成交量比: {row['day1_volume_ratio']:.2f}, 第2天成交量比: {row['day2_volume_ratio']:.2f}")
                          
                        result_dict = {
                            '策略名称': strategy_name,
                            '证券代码': row['security_id'],
                            '证券名称': row['security_name'],
                            '日期': row['trade_date'],
                            '收盘价': row['close'] * 0.0001,
                            '涨停次数': row['upper_times'],
                            '涨停日期': row['limit_up_date'],
                            '涨停价': row['limit_up_price'],
                            '第1天上影线': row['day1_upper_shadow_pct'],
                            '第2天下影线': row['day2_lower_shadow_pct'],
                            '第1天成交量比': row['day1_volume_ratio'],
                            '第2天成交量比': row['day2_volume_ratio']
                        }
                    elif strategy == 'limit_up_shrinking_volume':
                        logger.info(f"证券代码: {row['security_id']}, 日期: {row['trade_date']}, 收盘价: {row['close'] * 0.0001:.3f}")
                                   
                        result_dict = {
                            '策略名称': strategy_name,
                            '证券代码': row['security_id'],
                            '证券名称': row['security_name'],
                            '日期': row['trade_date'],
                            '收盘价': row['close'] * 0.0001,
                            '涨停次数': row['upper_times'],
                            '涨停日期': row['limit_up_date'],
                            '涨停价': row['limit_up_price']
                            # '次日成交量比': row['day1_volume_ratio'],
                            # '放量比': row['volume_spike_ratio'],
                            # '放量间隔天数': row['days_to_spike']
                        }
                    elif strategy == 'oversold_rebound_technical_turning_point':
                        # 重新计算技术指标以确定哪些条件被满足
                        
                        logger.info(f"证券代码: {row['security_id']}, 日期: {row['trade_date']}, 收盘价: {row['close'] * 0.0001:.3f}")
                        
                        result_dict = {
                            '策略名称': strategy_name,
                            '证券代码': row['security_id'],
                            '证券名称': row['security_name'],
                            '日期': row['trade_date'],
                            '收盘价': row['close'] * 0.0001,
                            '涨停次数': row['upper_times'],
                            '涨停日期': row['limit_up_date'],
                            '涨停价': row['limit_up_price']
                            # '近20日最大回撤': row['max_drawdown'],
                            # 'KDJ金叉': row['kdj_golden_cross'],
                            # 'RSI反弹': row['rsi_rebound'],
                            # '收复5日均线': row['ma5_recovery']
                        }
                    elif strategy == 'positive_net_inflow_3days':
                        logger.info(f"证券代码: {row['security_id']}, 日期: {row['trade_date']}, 收盘价: {row['close'] * 0.0001:.3f}")
                        logger.info(f"  最近3日净流入: {row['netamt'] * 0.0001:.2f}万元")
                        
                        result_dict = {
                            '策略名称': strategy_name,
                            '证券代码': row['security_id'],
                            '证券名称': row['security_name'],
                            '日期': row['trade_date'],
                            '收盘价': row['close'] * 0.0001,
                            '涨停次数': row['upper_times'],
                            '最近3日净流入(万元)': row['netamt'] * 0.0001
                        }
                    elif strategy == 'positive_big_net_inflow_3days':
                        logger.info(f"证券代码: {row['security_id']}, 日期: {row['trade_date']}, 收盘价: {row['close'] * 0.0001:.3f}")
                        logger.info(f"  最近3日主力净流入: {row['bignetamt'] * 0.0001:.2f}万元")
                        
                        result_dict = {
                            '策略名称': strategy_name,
                            '证券代码': row['security_id'],
                            '证券名称': row['security_name'],
                            '日期': row['trade_date'],
                            '收盘价': row['close'] * 0.0001,
                            '涨停次数': row['upper_times'],
                            '最近3日主力净流入(万元)': row['bignetamt'] * 0.0001
                        }
                    elif strategy == 'positive_net_and_big_net_inflow_3days':
                        logger.info(f"证券代码: {row['security_id']}, 日期: {row['trade_date']}, 收盘价: {row['close'] * 0.0001:.3f}")
                        logger.info(f"  最近3日净流入: {row['netamt'] * 0.0001:.2f}万元")
                        logger.info(f"  最近3日主力净流入: {row['bignetamt'] * 0.0001:.2f}万元")
                        
                        result_dict = {
                            '策略名称': strategy_name,
                            '证券代码': row['security_id'],
                            '证券名称': row['security_name'],
                            '日期': row['trade_date'],
                            '收盘价': row['close'] * 0.0001,
                            '涨停次数': row['upper_times'],
                            '最近3日净流入(万元)': row['netamt'] * 0.0001,
                            '最近3日主力净流入(万元)': row['bignetamt'] * 0.0001
                        }
                    else:
                        logger.info(f"证券代码: {row['security_id']}, 日期: {row['trade_date']}, 收盘价: {row['close'] * 0.0001:.3f}")
                        result_dict = {
                            '策略名称': strategy_name,
                            '证券代码': row['security_id'],
                            '证券名称': row['security_name'],
                            '日期': row['trade_date'],
                            '收盘价': row['close'] * 0.0001,
                            '涨停次数': row['upper_times']
                        }
                    
                    results.append(result_dict)
            else:
                logger.info("没有股票满足该策略条件")
        except Exception as e:
            logger.error(f"执行策略 {strategy_name} 时出错: {str(e)}")

        result_df = pd.DataFrame(results)
        result_df.to_csv("stock_select.csv", index=False, float_format='%.3f')